﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Urs.Admin.Models.Common;
using Urs.Admin.Models.Coupons;
using Urs.Admin.Models.Users;
using Urs.Admin.Models.ShoppingCart;
using Urs.Core;
using Urs.Data.Domain.Common;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Payments;
using Urs.Data.Domain.Shipping;
using Urs.Data.Domain.Configuration;
using Urs.Services;
using Urs.Services.Authentication.External;
using Urs.Services.Stores;
using Urs.Services.Common;
using Urs.Services.Users;
using Urs.Services.Directory;
using Urs.Services.ExportImport;
using Urs.Services.Localization;
using Urs.Services.Logging;
using Urs.Services.Media;
using Urs.Services.Orders;
using Urs.Services.Security;
using Urs.Framework;
using Urs.Framework.Controllers;
using Urs.Framework.Extensions;
using Urs.Framework.Kendoui;
using Urs.Framework.Mvc;
using Urs.Web.Infrastructure;
using Urs.Admin.Models.Common;

namespace Urs.Admin.Controllers
{
    [AdminAuthorize]
    public partial class UserController : BaseAdminController
    {
        #region Fields

        private readonly IUserService _userService;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly IUserRegistrationService _userRegistrationService;
        private readonly IUserReportService _userReportService;
        private readonly ILocalizationService _localizationService;
        private readonly RewardPointsSettings _rewardPointsSettings;
        private readonly IAddressService _addressService;
        private readonly UserSettings _userSettings;
        private readonly IWorkContext _workContext;
        private readonly IOrderService _orderService;
        private readonly IExportManager _exportManager;
        private readonly IGoodsSpecFormatter _goodsSpecFormatter;
        private readonly IActivityLogService _activityLogService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly IPermissionService _permissionService;
        private readonly AdminAreaSettings _adminAreaSettings;
        private readonly IOpenAuthenticationService _openAuthenticationService;
        private readonly AddressSettings _addressSettings;
        private readonly IAreaService _areaService;
        private readonly IPictureService _pictureService;
        #endregion

        #region Constructors

        public UserController(IUserService userService,
            IShoppingCartService shoppingCartService,
            IUserRegistrationService userRegistrationService,
            IUserReportService userReportService,
            ILocalizationService localizationService,
             RewardPointsSettings rewardPointsSettings,
            IAddressService addressService,
            UserSettings userSettings,
            IWorkContext workContext,
            IOrderService orderService, IExportManager exportManager,
            IGoodsSpecFormatter goodsSpecFormatter,
            IActivityLogService activityLogService,
            IPriceCalculationService priceCalculationService,
            IPermissionService permissionService, AdminAreaSettings adminAreaSettings,
            IOpenAuthenticationService openAuthenticationService,
            AddressSettings addressSettings,
            IAreaService areaService,
            IPictureService pictureService)
        {
            this._userService = userService;
            this._userRegistrationService = userRegistrationService;
            this._shoppingCartService = shoppingCartService;
            this._userReportService = userReportService;
            this._localizationService = localizationService;
            this._rewardPointsSettings = rewardPointsSettings;
            this._goodsSpecFormatter = goodsSpecFormatter;
            this._addressService = addressService;
            this._userSettings = userSettings;
            this._workContext = workContext;
            this._orderService = orderService;
            this._exportManager = exportManager;
            this._activityLogService = activityLogService;
            this._priceCalculationService = priceCalculationService;
            this._permissionService = permissionService;
            this._adminAreaSettings = adminAreaSettings;
            this._openAuthenticationService = openAuthenticationService;
            this._addressSettings = addressSettings;
            this._areaService = areaService;
            this._pictureService = pictureService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected string GetSystemRolesNames(IList<UserRole> userRoles, string separator = ",")
        {
            var sb = new StringBuilder();
            for (int i = 0; i < userRoles.Count; i++)
            {
                sb.Append(userRoles[i].Name);
                if (i != userRoles.Count - 1)
                {
                    sb.Append(separator);
                    sb.Append(" ");
                }
            }
            return sb.ToString();
        }

        [NonAction]
        protected IList<RegisteredReportLineModel> GetReportRegisteredUsersModel()
        {
            var report = new List<RegisteredReportLineModel>();
            report.Add(new RegisteredReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Users.Reports.RegisteredUsers.Fields.Period.7days"),
                Users = _userReportService.GetRegisteredUsersReport(7)
            });

            report.Add(new RegisteredReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Users.Reports.RegisteredUsers.Fields.Period.14days"),
                Users = _userReportService.GetRegisteredUsersReport(14)
            });
            report.Add(new RegisteredReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Users.Reports.RegisteredUsers.Fields.Period.month"),
                Users = _userReportService.GetRegisteredUsersReport(30)
            });
            report.Add(new RegisteredReportLineModel()
            {
                Period = _localizationService.GetResource("Admin.Users.Reports.RegisteredUsers.Fields.Period.year"),
                Users = _userReportService.GetRegisteredUsersReport(365)
            });

            return report;
        }

        [NonAction]
        protected IList<UserModel.AssociatedExternalAuthModel> GetAssociatedExternalAuthRecords(User user)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            var result = new List<UserModel.AssociatedExternalAuthModel>();
            foreach (var record in _openAuthenticationService.GetExternalIdentifiersFor(user))
            {
                var method = _openAuthenticationService.LoadExternalAuthenticationMethodBySystemName(record.ProviderSystemName);
                if (method == null)
                    continue;

                result.Add(new UserModel.AssociatedExternalAuthModel()
                {
                    Id = record.Id,
                    Name = record.Name,
                    OpenId = record.OpenId,
                    AvatarUrl = record.AvatarUrl,
                    AuthMethodName = method.PluginDescriptor.FriendlyName
                });
            }

            return result;
        }

        private string GetInviteCode(User user)
        {
            if (user == null)
                return "";
            if (user.AffiliateId == null)
                return "";
            if (user.AffiliateId.Value == 0)
                return "";

            var entity = _userService.GetUserById(user.AffiliateId.Value);
            if (entity == null) return "";

            return entity.GetFullName();
        }
        [NonAction]
        protected UserModel PrepareUserModelForList(User user, bool prepareAvatar)
        {
            var model = new UserModel()
            {
                Id = user.Id,
                Email = user.Email,
                PhoneNumber = user.PhoneNumber,
                FullName = user.GetFullName(),
                SystemRoleNames = GetSystemRolesNames(user.UserRoles.ToList()),
                LastIpAddress = user.LastIpAddress,
                Active = user.Active,
                Approved = user.Approved,
                InvitedCode = GetInviteCode(user),
                CreateTime = user.CreateTime,
                LastActivityDate = user.LastActivityTime,
                AdminComment = user.AdminComment,
                Nickname = user.Nickname
            };

            if (prepareAvatar)
            {
                model.AvatarPictureUrl = _pictureService.GetPictureUrl(user.AvatarPictureId, 100);
            }
            return model;
        }

        [NonAction]
        protected string ValidateRoles(IList<UserRole> userRoles)
        {
            if (userRoles == null)
                throw new ArgumentNullException("userRoles");

            //ensure a user is not added to both 'Guests' and 'Registered' user roles
            //ensure that a user is in at least one required role ('Guests' and 'Registered')
            bool isInGuestsRole = userRoles
                .Where(cr => cr.SystemName == SystemRoleNames.Guests)
                .FirstOrDefault() != null;
            bool isInRegisteredRole =
                userRoles
                .Where(cr => cr.SystemName == SystemRoleNames.Registered)
                .FirstOrDefault() != null;
            if (isInGuestsRole && isInRegisteredRole)
                return "The user cannot be in both 'Guests' and 'Registered' user roles";
            if (!isInGuestsRole && !isInRegisteredRole)
                return "Add the user to 'Guests' or 'Registered' user role";

            //no errors
            return "";
        }

        #endregion

        #region User

        public IActionResult Index()
        {
            return RedirectToAction("List");
        }

        public IActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            //load registered users by default
            var defaultRoleIds = new List<int> { _userService.GetUserRoleBySystemName(SystemRoleNames.Registered).Id };
            var listModel = new UserListModel()
            {
                SearchUserRoleIds = defaultRoleIds,
            };
            var allRoles = _userService.GetAllUserRoles(true);
            foreach (var role in allRoles)
            {
                listModel.AvailableUserRoles.Add(new SelectListItem
                {
                    Text = role.Name,
                    Value = role.Id.ToString(),
                    Selected = defaultRoleIds.Any(x => x == role.Id)
                });
            }

            return View(listModel);
        }

        [HttpPost]
        public IActionResult ListJson(PageRequest command, UserListModel model, int[] searchUserRoleIds)
        {
            //we use own own binder for searchUserRoleIds property 
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            searchUserRoleIds = new int[1] { _userService.GetUserRoleBySystemName(SystemRoleNames.Registered).Id };
            var users = _userService.GetAllUsers(userRoleIds: searchUserRoleIds, email: model.SearchEmail, nickname: model.SearchNickname,
              company: model.SearchCompany, phone: model.SearchPhone,
              pageIndex: command.Page - 1, pageSize: command.Limit,
              provinceName: model.ProvinceName, cityName: model.CityName, areaName: model.AreaName);
            var result = new ResponseResult
            {
                data = users.Select(x => PrepareUserModelForList(x, true)),
                count = users.TotalCount
            };
            return Json(result);
        }

        public IActionResult Create()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var model = new UserModel();

            model.AllowManagingUserRoles = _permissionService.Authorize(StandardPermissionProvider.ManageUserRoles);
            
            //default value
            model.Active = true;

            return View(model);
        }

        [HttpPost]
        [FormValueRequired("save", "save-continue")]
        public IActionResult Create(UserModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            if (!String.IsNullOrWhiteSpace(model.Email))
            {
                var cust2 = _userService.GetUserByEmail(model.Email);
                if (cust2 != null)
                    ModelState.AddModelError("", "Email is already registered");
            }

            //validate user roles
            var allUserRoles = _userService.GetAllUserRoles(true);
            var newUserRoles = new List<UserRole>();
            foreach (var userRole in allUserRoles)
                if (model.SelectedUserRoleIds != null && model.SelectedUserRoleIds.Contains(userRole.Id))
                    newUserRoles.Add(userRole);
            var userRolesError = ValidateRoles(newUserRoles);
            if (!String.IsNullOrEmpty(userRolesError))
                ModelState.AddModelError("", userRolesError);
            bool allowManagingUserRoles = _permissionService.Authorize(StandardPermissionProvider.ManageUserRoles);

            if (ModelState.IsValid)
            {
                var user = new User()
                {
                    UserGuid = Guid.NewGuid(),
                    Email = model.Email,
                    PhoneNumber = model.PhoneNumber,
                    AdminComment = model.AdminComment,
                    AvatarPictureId = model.AvatarPictureId,
                    Active = model.Active,
                    CreateTime = DateTime.Now,
                    Nickname = model.Nickname,
                    Approved = model.Approved,
                    LastActivityTime = DateTime.Now,
                };
                _userService.InsertUser(user);

                //password
                if (!String.IsNullOrWhiteSpace(model.Password))
                {
                    var changePassRequest = new ChangePasswordRequest(user.UserGuid, false, _userSettings.DefaultPasswordFormat, model.Password);
                    var changePassResult = _userRegistrationService.ChangePassword(changePassRequest);
                    if (!changePassResult.Success)
                    {
                        ModelState.AddModelError("", changePassResult.Errors.FirstOrDefault());
                    }
                }

                //user roles
                if (allowManagingUserRoles)
                {
                    foreach (var userRole in newUserRoles)
                        user.UserRoleMappings.Add(new UserRoleMapping { UserRole = userRole });
                    _userService.UpdateUser(user);
                }

                //activity log
                _activityLogService.InsertActivity("AddNewUser", _localizationService.GetResource("ActivityLog.AddNewUser"), user.Id);

                return continueEditing ? RedirectToAction("Edit", new { id = user.Id }) : RedirectToAction("List");
            }
            
            model.AllowManagingUserRoles = allowManagingUserRoles;

            return View(model);

        }
        public IActionResult Edit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(id);
            if (user == null || user.Deleted)
                //No user found with the specified id
                return RedirectToAction("List");

            if (user.IsAdmin())
            {
                if (!_permissionService.Authorize(StandardPermissionProvider.ManageAcl))
                    return HttpUnauthorized();
            }

            var model = new UserModel();
            model.Id = user.Id;
            model.Email = user.Email;
            model.Approved = user.Approved;
            model.PhoneNumber = user.PhoneNumber;
            model.UserGuid = user.UserGuid;
            model.AdminComment = user.AdminComment;
            model.Active = user.Active;
            model.AffiliateId = user.AffiliateId;
            if (user.AffiliateId.HasValue)
                model.AffilateName = _userService.GetUserById(user.AffiliateId.Value)?.Nickname;
            model.AvatarPictureId = user.AvatarPictureId;
            model.Code = user.Code;
            model.CreateTime = user.CreateTime;
            model.LastActivityDate = user.LastActivityTime;
            model.LastIpAddress = user.LastIpAddress;
            //form fields
            model.Nickname = user.Nickname;
            //user roles
            model.AvailableUserRoles = _userService
                .GetAllUserRoles(true)
                .Select(cr => {
                    var item = new SelectListItem() { Text = cr.Name, Value = cr.Id.ToString() };
                    return item;
                })
                .ToList();

            model.SelectedUserRoleIds = user.UserRoles.Select(cr => cr.Id).ToList();
           
            model.AllowManagingUserRoles = _permissionService.Authorize(StandardPermissionProvider.ManageUserRoles);
            //reward points gistory
            model.DisplayRewardPointsHistory = _rewardPointsSettings.Enabled;
            model.AddRewardPointsValue = 0;
            model.AddRewardPointsMessage = "积分赠送";
            //external authentication records
            model.AssociatedExternalAuthRecords = GetAssociatedExternalAuthRecords(user);

            return View(model);
        }

        [HttpPost]
        public IActionResult Edit(UserModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(model.Id);
            if (user == null || user.Deleted)
                return Json(new { error = 1 });

            if (ModelState.IsValid)
            {
                try
                {
                    user.AdminComment = model.AdminComment;
                    user.Active = model.Active;
                    user.Approved = model.Approved;
                    user.PhoneNumber = model.PhoneNumber;
                    //user.AvatarPictureId = model.AvatarPictureId;
                    user.Nickname = model.Nickname;
                    //email
                    user.Email = model.Email;

                    _userService.UpdateUser(user);

                    //activity log
                    _activityLogService.InsertActivity("EditUser", _localizationService.GetResource("ActivityLog.EditUser"), user.Id);

                    return Json(new { success = 1 });
                }
                catch (Exception exc)
                {
                    return Json(new { error = 1, msg = exc.Message });
                }
            }
            return Json(new { error = 1, msg = ModelState.FirstMessage() });
        }

        [HttpPost]
        public IActionResult ChangePassword(UserModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(model.Id);
            if (user == null)
                return Json(new { error = 1 });

            if (ModelState.IsValid)
            {
                var changePassRequest = new ChangePasswordRequest(user.UserGuid,
                    false, _userSettings.DefaultPasswordFormat, model.Password);
                var changePassResult = _userRegistrationService.ChangePassword(changePassRequest);
                if (changePassResult.Success)
                    return Json(new { success = 1 });
                else
                    return Json(new { error = 1, msg = changePassResult.Errors.FirstOrDefault() });
            }

            return Json(new { error = 1, msg = ModelState.FirstMessage() });
        }

        [HttpPost]
        public IActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(id);
            if (user == null)
                return Json(new { error = 1 });

            try
            {
                if (user.ExternalAuthRecords.Count() > 0)
                {
                    _openAuthenticationService.DeleteExternalAuthenticationRecord(user.ExternalAuthRecords.FirstOrDefault());
                }
                if (user.ExternalAuthRecords.Count() > 0)
                {
                    _openAuthenticationService.DeleteExternalAuthenticationRecord(user.ExternalAuthRecords.FirstOrDefault());
                }
                _userService.DeleteUser(user);
                //activity log
                _activityLogService.InsertActivity("DeleteUser", _localizationService.GetResource("ActivityLog.DeleteUser"), user.Id);

                return Json(new { success = 1 });
            }
            catch (Exception exc)
            {
                return Json(new { error = 1, msg = exc.Message });
            }
        }

        [HttpPost]
        public IActionResult Active(CheckboxModel checkbox)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(checkbox.Id);
            if (user == null)
                return Json(new { error = 1 });

            if (checkbox.Checked)
                user.Active = true;
            else
                user.Active = false;
            _userService.UpdateUser(user);

            return Json(new { success = 1 });
        }
        #endregion

        #region Points history

        [HttpPost]
        public IActionResult RewardPointsHistorySelect(int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                throw new ArgumentException("No user found with the specified id");

            var model = new List<UserModel.RewardPointsHistoryModel>();
            foreach (var rph in user.PointsHistory.OrderByDescending(rph => rph.CreateTime).ThenByDescending(rph => rph.Id))
            {
                model.Add(new UserModel.RewardPointsHistoryModel()
                {
                    Points = rph.Points,
                    PointsBalance = rph.PointsBalance,
                    Message = rph.Message,
                    CreateTime = rph.CreateTime
                });
            }
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }


        public IActionResult RewardPointsHistoryAdd(int userId, int addRewardPointsValue, string addRewardPointsMessage)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                return Json(new { Result = false });

            user.AddRewardPointsHistoryEntry(addRewardPointsValue, addRewardPointsMessage);
            _userService.UpdateUser(user);

            return Json(new { Result = true });
        }

        #endregion

        #region Addresses

        [HttpPost]
        public IActionResult AddressesSelect(int userId, PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                throw new ArgumentException("No user found with the specified id", "userId");

            var addresses = user.Addresses.OrderByDescending(a => a.CreateTime).ThenByDescending(a => a.Id).ToList();
            var result = new ResponseResult
            {
                data = addresses.Select(x =>
                {
                    var model = x.ToModel<AddressModel>();
                    var addressHtmlSb = new StringBuilder("<div>");
                    if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(model.Company))
                        addressHtmlSb.AppendFormat("{0}<br />", WebUtility.HtmlEncode(model.Company));
                    if (_addressSettings.StreetAddressEnabled && !String.IsNullOrEmpty(model.Address1))
                        addressHtmlSb.AppendFormat("{0}<br />", WebUtility.HtmlEncode(model.Address1));
                    if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(model.Address2))
                        addressHtmlSb.AppendFormat("{0}<br />", WebUtility.HtmlEncode(model.Address2));
                    if (_addressSettings.ProvincesEnabled)
                    {
                        if (!String.IsNullOrEmpty(model.ProvinceName))
                            addressHtmlSb.AppendFormat("{0},", WebUtility.HtmlEncode(model.ProvinceName));
                        if (!String.IsNullOrEmpty(model.CityName))
                            addressHtmlSb.AppendFormat("{0},", WebUtility.HtmlEncode(model.CityName));
                        if (!String.IsNullOrEmpty(model.AreaName))
                            addressHtmlSb.AppendFormat("{0}", WebUtility.HtmlEncode(model.AreaName));
                    }
                    var customAttributesFormatted = _goodsSpecFormatter.FormatAttributes(x?.CustomAttributes);
                    if (!string.IsNullOrEmpty(customAttributesFormatted))
                    {
                        //already encoded
                        addressHtmlSb.AppendFormat("<br />{0}", customAttributesFormatted);
                    }
                    if (_addressSettings.ZipPostalCodeEnabled && !String.IsNullOrEmpty(model.ZipPostalCode))
                        addressHtmlSb.AppendFormat("{0}<br />", WebUtility.HtmlEncode(model.ZipPostalCode));

                    addressHtmlSb.Append("</div>");
                    model.AddressHtml = addressHtmlSb.ToString();
                    return model;
                }),
                count = addresses.Count
            };
            return Json(result);
        }

        [HttpPost]
        public IActionResult AddressDelete(int id, int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                throw new ArgumentException("No user found with the specified id", "userId");

            var address = user.Addresses.Where(a => a.Id == id).FirstOrDefault();
            user.RemoveAddress(address);
            _userService.UpdateUser(user);
            //now delete the address record
            _addressService.DeleteAddress(address);

            return new NullJsonResult();
        }

        public IActionResult AddressCreate(int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                //No user found with the specified id
                return RedirectToAction("List");

            ViewBag.UserId = userId;

            var model = new AddressModel();
            PreparedUserAddress(model);

            return View(model);
        }

        [HttpPost]
        public IActionResult AddressCreate(AddressModel model, int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                //No user found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                var address = model.ToEntity<Address>();
                address.CreateTime = DateTime.Now;
                user.Addresses.Add(address);
                _userService.UpdateUser(user);

                return RedirectToAction("AddressEdit", new { addressId = address.Id, userId = userId });
            }

            //If we got this far, something failed, redisplay form
            ViewBag.UserId = userId;

            PreparedUserAddress(model);

            return View(model);
        }

        public IActionResult AddressEdit(int addressId, int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                //No user found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(addressId);
            if (address == null)
                //No address found with the specified id
                return RedirectToAction("Edit", new { id = user.Id });

            var model = new AddressModel();
            model = address.ToModel<AddressModel>();

            ViewBag.UserId = userId;

            PreparedUserAddress(model);
            return View(model);
        }

        [HttpPost]
        public IActionResult AddressEdit(AddressModel model, int userId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var user = _userService.GetUserById(userId);
            if (user == null)
                //No user found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(model.Id);
            if (address == null)
                //No address found with the specified id
                return RedirectToAction("Edit", new { id = user.Id });

            if (ModelState.IsValid)
            {
                address = model.ToEntity(address);
                _addressService.UpdateAddress(address);

                return RedirectToAction("AddressEdit", new { addressId = model.Id, userId = userId });
            }

            //If we got this far, something failed, redisplay form
            ViewBag.UserId = userId;

            model = address.ToModel<AddressModel>();
            PreparedUserAddress(model);

            return View(model);
        }

        [NonAction]
        public void PreparedUserAddress(AddressModel model)
        {
            model.NameEnabled = true;
            model.NameRequired = true;
            model.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.CompanyRequired = _addressSettings.CompanyRequired;
            model.ProvincesEnabled = _addressSettings.ProvincesEnabled;
            model.LongitudeEnabled = _addressSettings.LongitudeEnabled;
            model.LatitudeEnabled = _addressSettings.LatitudeEnabled;
            model.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.PhoneRequired = _addressSettings.PhoneRequired;
            model.FaxEnabled = _addressSettings.FaxEnabled;
            model.FaxRequired = _addressSettings.FaxRequired;
        }
        #endregion

        #region Orders

        [HttpPost]
        public IActionResult OrderList(int userId, PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var orders = _orderService.SearchOrders(userId: userId,pageIndex:command.Page-1,pageSize:command.Limit);

            var model = new ResponseResult
            {
                data = orders.OrderBy(x => x.CreateTime).Select(order =>
                    {
                        var orderModel = new UserModel.UserOrderModel();
                        orderModel.Id = order.Id;
                        orderModel.OrderStatusId = order.OrderStatusId;
                        orderModel.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService);
                        orderModel.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService);
                        orderModel.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService);
                        orderModel.OrderTotal = PriceFormatter.FormatPrice(order.OrderTotal);
                        orderModel.CreateTime = order.CreateTime;
                        return orderModel;
                    }),
                count = orders.Count
            };
            return Json(model);
        }

        #endregion

        #region Reports

        public IActionResult Reports()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var model = new UserReportsModel();
            //users by number of orders
            model.BestUsersByNumberOfOrders = new BestUsersReportModel();
            model.BestUsersByNumberOfOrders.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.BestUsersByNumberOfOrders.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestUsersByNumberOfOrders.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.BestUsersByNumberOfOrders.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestUsersByNumberOfOrders.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.BestUsersByNumberOfOrders.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //users by order total
            model.BestUsersByOrderTotal = new BestUsersReportModel();
            model.BestUsersByOrderTotal.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.BestUsersByOrderTotal.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestUsersByOrderTotal.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.BestUsersByOrderTotal.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            model.BestUsersByOrderTotal.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.BestUsersByOrderTotal.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            return View(model);
        }


        public IActionResult ReportBestUsersByOrderTotalList(PageRequest command, BestUsersReportModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)model.StartDate.Value;

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)model.EndDate.Value.AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;


            var items = _userReportService.GetBestUsersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, shippingStatus, 1);
            var result = new ResponseResult
            {
                data = items.Select(x =>
                {
                    var m = new BestUserReportLineModel()
                    {
                        UserId = x.UserId,
                        OrderTotal = PriceFormatter.FormatPrice(x.OrderTotal),
                        OrderCount = x.OrderCount,
                    };
                    var user = _userService.GetUserById(x.UserId);
                    if (user != null)
                    {
                        m.UserName = user.IsGuest()
                                             ? _localizationService.GetResource("Admin.Users.Guest")
                                             : user.GetFullName();
                    }
                    return m;
                }),
                count = items.Count
            };
            return Json(result);
        }

        public IActionResult ReportBestUsersByNumberOfOrdersList(PageRequest command, BestUsersReportModel model)
        {
            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)model.StartDate.Value;

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)model.EndDate.Value.AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;


            var items = _userReportService.GetBestUsersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, shippingStatus, 2);
            var result = new ResponseResult
            {
                data = items.Select(x =>
                {
                    var m = new BestUserReportLineModel()
                    {
                        UserId = x.UserId,
                        OrderTotal = PriceFormatter.FormatPrice(x.OrderTotal),
                        OrderCount = x.OrderCount,
                    };
                    var user = _userService.GetUserById(x.UserId);
                    if (user != null)
                    {
                        m.UserName = user.IsGuest()
                                             ? _localizationService.GetResource("Admin.Users.Guest")
                                             : user.Email;
                    }
                    return m;
                }),
                count = items.Count
            };
            return Json(result);
        }

        public IActionResult ReportRegisteredUsers()
        {
            var model = GetReportRegisteredUsersModel();
            return PartialView(model);
        }

        public IActionResult ReportRegisteredUsersList(PageRequest command)
        {
            var model = GetReportRegisteredUsersModel();
            var result = new ResponseResult
            {
                data = model,
                count = model.Count
            };
            return Json(result);
        }

        public IActionResult LoadUserStatistics(string period)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return Content("");

            var result = new List<object>();

            var nowDt = DateTime.Now;
            var searchUserRoleIds = new[] { _userService.GetUserRoleBySystemName(SystemRoleNames.Registered).Id };

            switch (period)
            {
                case "year":
                    //year statistics
                    var yearAgoRoundedDt = nowDt.AddYears(-1).AddMonths(1);
                    var searchYearDateUser = new DateTime(yearAgoRoundedDt.Year, yearAgoRoundedDt.Month, 1);

                    do
                    {
                        result.Add(new
                        {
                            date = searchYearDateUser.Date.ToString("Y"),
                            value = _userService.GetAllUsers(
                                searchYearDateUser,
                                searchYearDateUser.AddMonths(1),
                                userRoleIds: searchUserRoleIds, pageSize: 1).TotalCount.ToString()
                        });
                        searchYearDateUser = searchYearDateUser.AddMonths(1);

                    } while (!(searchYearDateUser.Year == nowDt.Year && searchYearDateUser.Month > nowDt.Month));
                    break;

                case "month":
                    //month statistics
                    var searchMonthDateUser = new DateTime(nowDt.Year, nowDt.AddMonths(-1).Month, nowDt.AddMonths(-1).Day);

                    do
                    {
                        result.Add(new
                        {
                            date = searchMonthDateUser.Date.ToString("M"),
                            value = _userService.GetAllUsers(
                                searchMonthDateUser,
                                searchMonthDateUser.AddDays(1),
                                userRoleIds: searchUserRoleIds,
                                pageSize: 1).TotalCount.ToString()
                        });
                        searchMonthDateUser = searchMonthDateUser.AddDays(1);

                    } while (!(searchMonthDateUser.Month == nowDt.Month && searchMonthDateUser.Day == nowDt.Day));

                    break;

                case "week":
                default:
                    //week statistics
                    var searchWeekDateUser = new DateTime(nowDt.Year, nowDt.AddDays(-7).Month, nowDt.AddDays(-7).Day);

                    do
                    {
                        result.Add(new
                        {
                            date = searchWeekDateUser.Date.ToString("d dddd"),
                            value = _userService.GetAllUsers(
                                searchWeekDateUser,
                                searchWeekDateUser.AddDays(1),
                                userRoleIds: searchUserRoleIds, pageSize: 1).TotalCount.ToString()
                        });
                        searchWeekDateUser = searchWeekDateUser.AddDays(1);

                    }
                    while (searchWeekDateUser < nowDt);

                    break;
            }

            return Json(result);
        }
        #endregion

        #region Current shopping cart/ wishlist


        public IActionResult GetCartList(int userId)
        {
            var user = _userService.GetUserById(userId);
            var cart = user.ShoppingCartItems.ToList();

            var result = new ResponseResult()
            {
                data = cart.Select(sci =>
                {
                    var sciModel = new ShoppingCartItemModel()
                    {
                        Id = sci.Id,
                        GoodsId = sci.GoodsId,
                        Quantity = sci.Quantity,
                        FullGoodsName = sci.Goods.Name,
                        AttributeInfo = _goodsSpecFormatter.FormatAttributes(sci.AttributesXml),
                        UnitPrice = PriceFormatter.FormatPrice(_priceCalculationService.GetUnitPrice(sci)),
                        Total = PriceFormatter.FormatPrice(_priceCalculationService.GetSubTotal(sci)),
                        UpdatedOn = sci.UpdateTime
                    };
                    return sciModel;
                }),
                count = cart.Count
            };
            return Json(result);
        }

        #endregion

        #region Activity log

        [HttpPost]
        public JsonResult ListActivityLog(PageRequest command, int userId)
        {
            var types = _activityLogService.GetAllActivityTypes();
            var activityLog = _activityLogService.GetAllActivities(null, null, userId, 0, command.Page - 1, command.Limit);
            var result = new ResponseResult
            {
                data = activityLog.Select(x =>
                {
                    var m = new UserModel.ActivityLogModel()
                    {
                        Id = x.Id,
                        ActivityLogTypeName = types.FirstOrDefault(t=>t.Id==x.ActivityLogTypeId)?.Name,
                        Comment = x.Comment,
                        CreateTime = x.CreateTime
                    };
                    return m;

                }),
                count = activityLog.TotalCount
            };
            return Json(result);
        }

        #endregion

        #region Export / Import
        [HttpPost, ActionName("List")]
        [FormValueRequired("exportexcel-all")]
        public IActionResult ExportExcelAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            try
            {
                var defaultRoleIds = new List<int> { _userService.GetUserRoleBySystemName(SystemRoleNames.Registered).Id };
                var users = _userService.GetAllUsers(userRoleIds: defaultRoleIds.ToArray());

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _exportManager.ExportUsersToXlsx(stream, users);
                    bytes = stream.ToArray();
                }
                return File(bytes, "text/xls", "users.xlsx");
            }
            catch (Exception exc)
            {
                return RedirectToAction("List");
            }
        }

        public IActionResult ExportExcelSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();

            var users = new List<User>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                users.AddRange(_userService.GetUsersByIds(ids));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _exportManager.ExportUsersToXlsx(stream, users);
                bytes = stream.ToArray();
            }
            return File(bytes, "text/xls", "users.xlsx");
        }

        #endregion

        public IActionResult InvitationCodeMessage()
        {
            var model = new MessageRecodeListModel();
            return View(model);
        }
        [HttpPost]
        public IActionResult InvitationCodeMessageList(PageRequest command, MessageRecodeListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageUsers))
                return HttpUnauthorized();


            var messages = _userService.GetMessageRecords(model.UserId, model.Phone, model.StartTime, model.EndTime, command.Page - 1, command.Limit);
            var result = new ResponseResult
            {
                data = messages.Select(x =>
                {
                    var m = new MessageRecodeItemModel();
                    m.Id = x.Id;
                    m.Mark = x.Mark;
                    m.Phone = x.Phone;
                    m.SendTime = x.SendTime;
                    m.Content = x.Content;
                    m.UserId = x.UserId;
                    m.UserName = x.UserId > 0 ? _userService.GetUserById(x.UserId).GetFullName() : "";
                    return m;
                }),
                count = messages.TotalCount
            };
            return Json(result);
        }
    }
}
